package com.liumingtai.learn.shiro.utils;


import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.crypto.MacProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.UUID;

/**
 * Created by
 *
 * @author Honey
 */
@Slf4j
@SuppressWarnings("all")
public class JwtHelper {
    public final static Long TOKEN_EXPIRED_TIME = 3600 * 24L;
    /**
     * JWT 加解密类型
     */
    static final SignatureAlgorithm JWT_ALG = SignatureAlgorithm.HS256;
    /**
     * JWT 生成密钥使用的密码
     */
    final static String JWT_RULE = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxqECh8RPhXlkMAKpaPGhHPZR55UNpyWydpYXo4wj1PgHHmEmgKc0abR3TnQtqRUjEj500ltG1kTQlN246OxMIo4sK8BQboKOQtTJF3aB1dGav++uXUolhT9gI0T55viYetfBe3Xwj9tJcuICq0ylmxCVTx+1GoA8QjQbTsdhCwIDAQAB";

    /**
     * JWT 添加至HTTP HEAD中的前缀
     */
    final static String JWT_SEPARATOR = "Bearer ";

    /**
     * 使用JWT默认方式，生成加解密密钥
     *
     * @param alg 加解密类型
     * @return
     */
    public static SecretKey generateKey(SignatureAlgorithm alg) {
        return MacProvider.generateKey(alg);
    }

    /**
     * 使用指定密钥生成规则，生成JWT加解密密钥
     *
     * @param alg  加解密类型
     * @param rule 密钥生成规则
     * @return
     */
    public static SecretKey generateKey(SignatureAlgorithm alg, String rule) {
        // 将密钥生成键转换为字节数组
        byte[] bytes = Base64.decodeBase64(rule);
        // 根据指定的加密方式，生成密钥
        return new SecretKeySpec(bytes, alg.getJcaName());
    }

    /**
     * 构建JWT
     *
     * @param alg      jwt 加密算法
     * @param key      jwt 加密密钥
     * @param sub      jwt 面向的用户
     * @param aud      jwt 接收方
     * @param jti      jwt 唯一身份标识
     * @param iss      jwt 签发者
     * @param duration jwt 有效时间，单位：秒
     * @param claims   jwt 私有声明
     * @return JWT字符串
     */
    public static String buildJWT(SignatureAlgorithm alg, Key key, String sub, String aud, String jti, String iss, Integer duration, Map<String, Object> claims) {
        // jwt的签发时间&生效时间
        LocalDateTime iat = LocalDateTime.now();
        // jwt的过期时间，这个过期时间必须要大于签发时间
        LocalDateTime exp = null;
        if (duration != null) {
            exp = iat.plusSeconds(duration);
        }
        // 获取JWT字符串

        String compact = Jwts.builder()
                .setClaims(claims)
                .signWith(alg, key)
                .setSubject(sub)
                .setAudience(aud)
                .setId(jti)
                .setIssuer(iss)
                .setNotBefore(DateUtil.asDate(iat))
                .setIssuedAt(DateUtil.asDate(iat))
                .setExpiration(exp != null ? DateUtil.asDate(exp) : null)
                .compact();
        // 在JWT字符串前添加"Bearer "字符串，用于加入"Authorization"请求头
        return JWT_SEPARATOR + compact;
    }

    /**
     * 构建JWT
     *
     * @param sub      jwt 面向的用户
     * @param aud      jwt 接收方
     * @param jti      jwt 唯一身份标识
     * @param iss      jwt 签发者
     * @param nbf      jwt 生效日期时间
     * @param duration jwt 有效时间，单位：秒
     * @param claims   jwt 私有声明
     * @return JWT字符串
     */
    public static String buildJWT(String sub, String aud, String jti, String iss, Integer duration, Map<String, Object> claims) {
        return buildJWT(JWT_ALG, generateKey(JWT_ALG, JWT_RULE), sub, aud, jti, iss, duration, claims);
    }

    /**
     * 构建JWT
     *
     * @param sub jwt 面向的用户
     * @param jti jwt 唯一身份标识，主要用来作为一次性token,从而回避重放攻击
     * @return JWT字符串
     */
    public static String buildJWT(String sub, String jti, Integer duration) {
        return buildJWT(sub, null, jti, null, duration, null);
    }

    /**
     * 构建JWT
     * &lt;p&gt;使用 UUID 作为 jti 唯一身份标识&lt;/p&gt;
     * &lt;p&gt;JWT有效时间 7200 秒，即 两小时 分钟&lt;/p&gt;
     *
     * @param sub jwt 面向的用户
     * @return JWT字符串
     */
    public static String buildJWT(String sub) {
        return buildJWT(sub, null, UUID.randomUUID().toString(), null, 7200, null);
    }

    /**
     * 解析JWT
     *
     * @param key       jwt 加密密钥
     * @param claimsJws jwt 内容文本
     * @return {@link Jws}
     * @throws Exception
     */

    public static Jws<Claims> parseJWT(Key key, String claimsJws) {
        // 移除 JWT 前的"Bearer "字符串
        claimsJws = StringUtils.substringAfter(claimsJws, JWT_SEPARATOR);
        // 解析 JWT 字符串
        return Jwts.parser().setSigningKey(key).parseClaimsJws(claimsJws);
    }

    /**
     * 校验JWT
     *
     * @param claimsJws jwt 内容文本
     * @return ture or false
     */
    public static Boolean checkJWT(String claimsJws) {
        boolean flag = false;
        try {
            SecretKey key = generateKey(JWT_ALG, JWT_RULE);
            // 获取 JWT 的 payload 部分
            System.out.println((parseJWT(key, claimsJws).getBody()));
            flag = (parseJWT(key, claimsJws).getBody() != null);
        } catch (Exception e) {
            log.warn("JWT验证出错，错误原因：{}", e.getMessage());
            if (e instanceof ExpiredJwtException) {
                throw e;
            }
        }
        return flag;
    }

    /**
     * 校验JWT
     *
     * @param key       jwt 加密密钥
     * @param claimsJws jwt 内容文本
     * @param sub       jwt 面向的用户
     * @return ture or false
     */
    public static Boolean checkJWT(Key key, String claimsJws, String sub) {
        boolean flag = false;
        try {
            // 获取 JWT 的 payload 部分
            Claims claims = parseJWT(key, claimsJws).getBody();
            // 比对JWT中的 sub 字段
            flag = claims.getSubject().equals(sub);
        } catch (Exception e) {
            log.warn("JWT验证出错，错误原因：{}", e.getMessage());
        }
        return flag;
    }

    /**
     * 校验JWT
     *
     * @param claimsJws jwt 内容文本
     * @param sub       jwt 面向的用户
     * @return ture or false
     */
    public static Boolean checkJWT(String claimsJws, String sub) {
        return checkJWT(generateKey(JWT_ALG, JWT_RULE), claimsJws, sub);
    }

    public static void main(String[] args) {
        System.out.println(buildJWT("xqg"));
    }

    /**
     * 构建JWT
     * &lt;p&gt;使用 UUID 作为 jti 唯一身份标识&lt;/p&gt;
     * &lt;p&gt;JWT有效时间 7200 秒，即 两小时 分钟&lt;/p&gt;
     *
     * @param sub jwt 面向的用户
     * @return JWT字符串
     */
    public static String buildJWT(String sub, Map<String, Object> claims) {
        return buildJWT(sub, null, UUID.randomUUID().toString(), null, 7200, claims);
    }

    /**
     * 构建JWT
     * &lt;p&gt;使用 UUID 作为 jti 唯一身份标识&lt;/p&gt;
     * &lt;p&gt;JWT有效时间 7200 秒，即 两小时 分钟&lt;/p&gt;
     *
     * @param sub      jwt 面向的用户
     * @param claims   jwt body
     * @param duration jwt 过期时间
     * @return JWT字符串
     */
    public static String buildJWT(String sub, Map<String, Object> claims, Integer duration) {
        return buildJWT(sub, null, UUID.randomUUID().toString(), null, duration, claims);
    }

}
